# -*- coding: utf-8 -*-
"""
    ui
    ~~

    User Interface.

    :copyright: 2010 by Baptiste Lepilleur.
    :license: Apache License 2.0, see LICENSE.txt for more details.
"""
from google.appengine.api import memcache
from google.appengine.ext import db
import logging
import projectmanager
from tipfy import NotFound
from tipfy import RequestHandler
from tipfy import Response
from tipfy import Unauthorized
from tipfy import request
from tipfy import url_for
from tipfy.ext.db import get_by_key_name_or_404
from tipfy.ext.db import get_entity_from_protobuf
from tipfy.ext.db import get_property_dict
from tipfy.ext.db import get_protobuf_from_entity
from tipfy.ext.jinja2 import Jinja2Mixin
from tipfy.ext.jinja2 import get_env
import uuid

def reserve_next_db_id(model):
  """Reserves one id from the datastore for the specified model.
  This is typically for use with "new" model form. This allows the
  id to be provided before the model is actually inserted.
  """
  model_key = db.Key.from_path('Project', 1)
  return db.allocate_ids(model_key, 1)[0]

def get_default_property_dict(entity_kind):
  default_properties = {}
  for name, property in entity_kind.properties().iteritems():
    value = property.default_value()
    if value is None:
      value = ''
    default_properties[name] = value
  return default_properties

def get_entity_property_dict(entity):
  properties = {}
  for name, value in get_property_dict(entity).iteritems():
    if value is None:
      value = ''
    properties[name] = value
  properties['key'] = entity.key().id()
  return properties

def numeric_datetime_format(value, format='%Y/%m/%d %H:%M.%S'):
  return value.strftime(format)

def verbose_datetime_format(value, format='%A %d %B %Y %H:%M.%S'):
  return value.strftime(format)

class RenderRequestHandler(RequestHandler, Jinja2Mixin):
  def __init__(self, app, context):
    self.app = app
    self.request = request
    self.request.context = {
    }
    self.context = self.request.context
    jinja2_env = get_env()
    jinja2_env.filters['verbose_datetime_format'] = verbose_datetime_format
    jinja2_env.filters['numeric_datetime_format'] = numeric_datetime_format
    #jinja2_env.trim_blocks = True # strip new-line after {% blocks %}

  def render_response(self, path, ** kwargs):
    self.context.update({
                        })
    return Jinja2Mixin.render_response(self, path, ** kwargs)

def get_project_list_properties():
  list_properties = []
  for project in projectmanager.Project.all().order('slug_name'):
    properties = {}
    properties['key'] = project.key().id()
    entity_properties = get_entity_property_dict(project)
    for name in ('slug_name', 'name'):
      properties[name] = entity_properties[name]
    list_properties.append( properties )
  return list_properties

class ProjectListHandler(RenderRequestHandler):
  def get(self):
    """Renders the list of projects."""
    self.context.update( {
      'breadcrumb': [
        ('Home', url_for('home')),
        ('Projects', None)
      ],
      'list_data': get_project_list_properties()
    } )
    return self.render_response('project_list.html')


def save_project_from_request():
  data = request.form
  key_id = int(data['key'])
  def txn():
    entity = projectmanager.Project.get_by_id(key_id)
    if entity is None:
      entity = projectmanager.Project(key=db.Key.from_path('Project', key_id),
                                      slug_name=data['slug_name'],
                                      name=data['name'])
    else:
      entity.slug_name = data['slug_name']
      entity.name = data['name']
    entity.put()
    return entity
  project = db.run_in_transaction(txn)
  return project

def extract_edit_action():
  """Returns tuple (should_save, is_new, should_redirect).
  """
  if '_action_new' in request.form:
    return (False, True, False)
  elif '_action_save' in request.form:
    return (True, False, True)
  elif '_action_continue' in request.form:
    return (True, False, False)
  elif '_action_add_another' in request.form:
    return (True, True, False)
  elif '_action_edit' in request.form:
    return (False, False, False)
  raise ValueError('Expected action missing in request.form')

def validate_entity_from_form(entity_kind):
  """Validates the form value for the given entity kind.
  Returns a dict{ field_name: error message } for each
  validation error. The dict is empty is no validation error occurred.
  """
  field_errors = {}
  for name, property in entity_kind.properties().iteritems():
    if name not in entity_kind.editable_properties:
      continue
    value = request.form[name]
    try:
      property.validate(value)
    except ValueError, e:
      field_errors[name] = unicode(e)
  return field_errors

def get_entity_properties_from_request( entity_kind ):
  properties = get_default_property_dict(entity_kind)
  data = request.form
  properties['key'] = int(data['key'])
  for name in entity_kind.editable_properties:
    properties[name] = data[name]
  return properties

class EditProjectHandler(RenderRequestHandler):
  def post(self):
    """Renders the form to add a new project."""
    entity_kind = projectmanager.Project
    should_save, is_new, should_redirect = extract_edit_action()
    message, error_message = '', ''
    field_errors = {}
    if should_save:
      field_errors = validate_entity_from_form(entity_kind)
      if not field_errors:
        entity = save_project_from_request()
        message = 'Project "%s" was successfully saved' % entity.name
      else:
        error_message = 'Some fields have non valid value. See error message below.'
    if not field_errors:
      if is_new:
        properties = get_default_property_dict(entity_kind)
        properties['key'] = reserve_next_db_id(entity_kind)
      else:
        if not should_save: # edit action
          key_id = int(request.form['key'])
          entity = projectmanager.Project.get_by_id(key_id)
        properties = get_entity_property_dict(entity)
    else:
      properties = get_entity_properties_from_request( entity_kind )
    self.context.update( {
      'breadcrumb': [
        ('Home', url_for('home')),
        ('Projects', url_for('project_list')),
        ('New Project', None)
      ],
      'post_uuid': uuid.uuid1().hex,
      'action_title': 'New Project',
      'message': message,
      'error_message': error_message,
      'project': properties,
      'field_errors': field_errors
    } )
    return self.render_response('project_edit.html')

class ViewProjectHandler(RenderRequestHandler):
  def get(self, slug_name=None):
    entity = projectmanager.get_by_slug_name( slug_name )
    if entity is None:
      raise NotFound('No project with slug_name = "%s"' % slug_name)
    properties = get_entity_property_dict(entity)
    self.context.update( {
      'breadcrumb': [
        ('Home', url_for('home')),
        ('Projects', url_for('project_list')),
        (entity.name, None)
      ],
      'action_title': 'View Project',
      'project': properties
    } )
    return self.render_response('project_view.html')
